package withdraw

import (
	"context"
	"errors"
	"strconv"
	"time"

	"gitee.com/liuxuezhan/ar-platform/assets-rpc/internal/dao/model/query"
	"gitee.com/liuxuezhan/ar-platform/assets-rpc/internal/dao/schema/user"
	modelWithdraw "gitee.com/liuxuezhan/ar-platform/assets-rpc/internal/dao/schema/withdraw"
	"gitee.com/liuxuezhan/ar-platform/assets-rpc/pkg/xerrors"
	"gitee.com/liuxuezhan/ar-platform/assets-rpc/rpc"
	"gitee.com/liuxuezhan/ar-platform/base-common/tool/wlog"
	"gitee.com/liuxuezhan/ar-platform/base-common/tool/wtime"
	"github.com/zeromicro/go-zero/core/logx"
	"gorm.io/gorm"
)

type WithdrawModel struct {
	ctx context.Context
	db  *gorm.DB
	logx.Logger
}

func NewWithdrawModel(ctx context.Context, db *gorm.DB) *WithdrawModel {
	return &WithdrawModel{
		ctx:    ctx,
		db:     db,
		Logger: logx.WithContext(ctx),
	}
}

func (l *WithdrawModel) WithdrawFail(in *rpc.FailWithDrawReq) error {
	err := l.db.Transaction(func(tx *gorm.DB) error {
		l.db = tx
		g := query.Use(l.db)
		num, _ := strconv.ParseInt(in.OrderNo, 10, 64)
		h := g.WithdrawOrder.
			Where(g.WithdrawOrder.ID.Eq(num)).
			Where(g.WithdrawOrder.Status.Eq(int32(rpc.WithDrawStatus_Pass)))
		d, err := h.First()
		if err != nil {
			return xerrors.ErrWithDrawOrderNot
		}
		d.Status = int32(rpc.WithDrawStatus_Fail)
		d.PayRemark = in.Data
		err2 := h.Save(d)
		if err2 != nil {
			return xerrors.ErrWithDrawOrderNotSave
		}
		// ---------------------------------检查余额----------------------------------------------
		err = l.addBalance(tx, d.UserId, d.OrderAmount)
		if err != nil {
			return err
		}

		// withdraw.OrderNo = "p" + strconv.Itoa(now.Year()) + fmt.Sprintf("10d", strconv.FormatInt(withdraw.ID, 10))
		err = l.adduserbalanceDatail(d)
		if err != nil {
			return xerrors.ErrWithDrawUserWalletLogNotSave
		}

		return nil
	})
	return err
}
func (l *WithdrawModel) WithdrawLock(in *rpc.LockWithDrawReq) (*rpc.LockWithDrawRes, error) {
	// todo: add your logic here and delete this line

	ret := &rpc.LockWithDrawRes{}
	err := l.db.Transaction(func(tx *gorm.DB) error {
		g := query.Use(tx)
		for i, _ := range in.OrderNos {
			AssetsCWithdrawOrder, err := g.WithdrawOrder.
				Where(g.WithdrawOrder.OrderNo.Eq(in.OrderNos[i])).
				Where(g.WithdrawOrder.Status.Eq(int32(rpc.WithDrawStatus_UnLock))).
				First()
			if err != nil {
				return xerrors.ErrWithDrawOrderNot
			} else {
				AssetsCWithdrawOrder.Locker = in.Locker
				AssetsCWithdrawOrder.Status = int32(rpc.WithDrawStatus_Lock)
				err = g.WithdrawOrder.Save(AssetsCWithdrawOrder)
				if err != nil {
					return xerrors.ErrWithDrawOrderNotSave
				}
			}
			ret.Data = append(ret.Data, &rpc.WithdrawInfo{
				OrderNo: in.OrderNos[i],
				Status:  AssetsCWithdrawOrder.Status,
				ErrCode: ret.ResHead.Code,
			})
		}

		return nil
	})
	if err != nil {
		return nil, err
	}
	// ------------------------------------第三方支付(异步回调)----------------------------------------------
	return ret, nil
}
func (l *WithdrawModel) WithdrawListPage(in *rpc.FindWithDrawReq) (*rpc.FindWithDrawRes, error) {
	ret := &rpc.FindWithDrawRes{}
	if in.Page.Page <= 0 {
		in.Page.Page = 1
	}
	if in.Page.PageSize <= 0 {
		in.Page.PageSize = 10
	}
	g := query.Use(l.db)
	var h query.IWithdrawOrderDo
	var AssetsCWithdrawOrder []*modelWithdraw.WithdrawOrder
	var err error
	if in.OrderNo != "" {
		h = g.WithdrawOrder.Where(g.WithdrawOrder.OrderNo.Eq(in.OrderNo))
	} else {
		if in.Locker != "" {
			h = g.WithdrawOrder.Where(g.WithdrawOrder.Locker.Eq(in.Locker))

		}
		if in.UserId != 0 {
			h = h.Where(g.WithdrawOrder.UserId.Eq(in.UserId))
		}
		if in.Type != "" {
			h = h.Where(g.WithdrawOrder.PayType.Eq(in.Type))
		}
		if in.Status != -1 {
			h = h.Where(g.WithdrawOrder.Status.Eq(in.Status))
		}
		if in.CollectionName != "" {
			h = h.Where(g.WithdrawOrder.CollectionName.Eq(in.CollectionName))
		}

		if in.MixAmount < in.MaxAmount {
			h = h.Where(g.WithdrawOrder.OrderAmount.Between(in.MixAmount, in.MaxAmount))
		}
		start, err := time.Parse(wtime.Format, in.StartTm)
		if err != nil {
			wlog.Handle.Errorf(l.ctx, "[%d:%d]:%s\n", in.UserId, ret.ResHead.Code, err.Error())
		}
		end, err := time.Parse(wtime.Format, in.EndTm)
		if err != nil {
			wlog.Handle.Errorf(l.ctx, "[%d:%d]:%s\n", in.UserId, ret.ResHead.Code, err.Error())
		}
		h = h.Where(g.WithdrawOrder.CreatedAt.Between(start.Unix(), end.Unix()))
	}

	if in.Page.SortOrder == "asc" {
		AssetsCWithdrawOrder, err = h.Order(g.WithdrawOrder.CreatedAt.Asc()).Limit(int(in.Page.PageSize)).Find()
	} else {
		AssetsCWithdrawOrder, err = h.Order(g.WithdrawOrder.CreatedAt.Desc()).Limit(int(in.Page.PageSize)).Find()

	}
	if err != nil {
		wlog.Handle.Errorf(l.ctx, "[%d:%d]:%s\n", in.UserId, ret.ResHead.Code, err.Error())
		return ret, err
	}
	start := (in.Page.Page - 1) * in.Page.PageSize // -------开始序号----------------
	if in.Page.PageSize > int64(len(AssetsCWithdrawOrder)) {
		in.Page.PageSize = int64(len(AssetsCWithdrawOrder))
	}
	// ret.Data = make([]*rpc.WithdrawInfo, in.Page.PageSize)
	for i := start; i < start+in.Page.PageSize; i++ {
		ret.Data = append(ret.Data, &rpc.WithdrawInfo{
			OrderNo:     AssetsCWithdrawOrder[i].OrderNo,
			UserId:      AssetsCWithdrawOrder[i].UserId,
			Amount:      AssetsCWithdrawOrder[i].OrderAmount,
			Type:        AssetsCWithdrawOrder[i].PayType,
			Account:     AssetsCWithdrawOrder[i].AccNo,
			ChannelCode: AssetsCWithdrawOrder[i].AccType,
			Status:      AssetsCWithdrawOrder[i].Status,
			CreatedAt:   AssetsCWithdrawOrder[i].CreatedAt,
		})
	}
	return ret, nil
}

func (l *WithdrawModel) addBalance(tx *gorm.DB, userId int64, amount float64) error {
	var userBalance user.UserWallet
	if err := tx.Where("user_id = ?", userId).First(&userBalance).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			// user not exist
			return xerrors.ErrNoRows
		} else {
			return xerrors.New(err)
		}
	}

	result := tx.Model(&userBalance).Where(map[string]interface{}{
		"user_id": userBalance.UserId,
		"version": userBalance.Version,
	}).
		Updates(map[string]interface{}{
			"balance": gorm.Expr("balance + ?", amount),
			"version": gorm.Expr("version + ?", 1),
		})

	if result.Error != nil {
		return xerrors.New(result.Error)
	}
	if result.RowsAffected == 0 {
		return xerrors.ErrNoRowsAffected
	}
	return nil
}
func (l *WithdrawModel) adduserbalanceDatail(Withdraw *modelWithdraw.WithdrawOrder) error {
	g := query.Use(l.db)
	UserBalanceDetail := &user.UserWalletLog{ // ---增加变动详情------------------------------------------
		UserId:   Withdraw.UserId,
		Amount:   Withdraw.OrderAmount,
		SourceId: Withdraw.OrderNo,
		TypeID:   20,
	}
	return g.UserWalletLog.Create(UserBalanceDetail)
}
func (l *WithdrawModel) Withdraw(in *rpc.UserWithDrawReq) (*rpc.UserWithDrawRes, error) {
	// todo: add your logic here and delete this line

	ret := &rpc.UserWithDrawRes{}

	err := l.db.Transaction(func(tx *gorm.DB) error {
		l.db = tx
		// ---------------------------------检查余额----------------------------------------------
		err := l.DeductBalance(tx, in.UserId, in.Amount)
		if err != nil {
			return err
		}

		Withdraw := &modelWithdraw.WithdrawOrder{ // 创建提现订单
			UserId:      in.UserId,
			PayType:     in.Type,
			AccType:     in.ChannelCode,
			OrderAmount: in.Amount,
			Status:      int32(rpc.WithDrawStatus_UnLock),
		}
		g := query.Use(l.db)
		err = g.WithdrawOrder.Create(Withdraw)
		if err != nil {
			return xerrors.ErrWithDrawOrderNotSave
		}
		Withdraw.OrderNo = strconv.FormatInt(Withdraw.ID, 10)
		// withdraw.OrderNo = "p" + strconv.Itoa(now.Year()) + fmt.Sprintf("10d", strconv.FormatInt(withdraw.ID, 10))
		err = l.adduserbalanceDatail2(Withdraw)
		if err != nil {
			return xerrors.ErrWithDrawUserWalletLogNotSave
		}

		ret.Data = &rpc.WithdrawInfo{
			UserId:      Withdraw.UserId,
			Type:        Withdraw.PayType,
			ChannelCode: Withdraw.AccType,
			Amount:      Withdraw.OrderAmount,
			Status:      Withdraw.Status,
			OrderNo:     Withdraw.OrderNo,
		}
		return nil
	})
	if err != nil {
		return nil, err
	}
	return ret, nil
}
func (l *WithdrawModel) adduserbalanceDatail2(Withdraw *modelWithdraw.WithdrawOrder) error {
	g := query.Use(l.db)
	UserBalanceDetail := &user.UserWalletLog{ // ---增加变动详情------------------------------------------
		UserId:   Withdraw.UserId,
		Amount:   Withdraw.OrderAmount,
		SourceId: Withdraw.OrderNo,
		TypeID:   21,
	}
	return g.UserWalletLog.Create(UserBalanceDetail)
}
func (l *WithdrawModel) DeductBalance(tx *gorm.DB, userId int64, amount float64) error {
	var userBalance user.UserWallet
	if err := tx.Where("user_id = ?", userId).First(&userBalance).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			// user not exist
			return xerrors.ErrNoRows
		} else {
			return xerrors.New(err)
		}
	}
	result := tx.Model(&userBalance).Where(map[string]interface{}{
		"user_id": userBalance.UserId,
		"version": userBalance.Version,
		"balance": gorm.Expr("balance >= ?", amount),
	}).
		Updates(map[string]interface{}{
			"balance": gorm.Expr("balance - ?", amount),
			"version": gorm.Expr("version + ?", 1),
		})
	if result.Error != nil {
		return xerrors.New(result.Error)
	}
	if result.RowsAffected == 0 {
		return xerrors.ErrNoRowsAffected
	}
	return nil
}
func (l *WithdrawModel) WithdrawPass(in *rpc.PassWithDrawReq) (*rpc.PassWithDrawRes, error) {
	// todo: add your logic here and delete this line

	ret := &rpc.PassWithDrawRes{}
	g := query.Use(l.db)
	num, _ := strconv.ParseInt(in.OrderNos, 10, 64)
	_, err2 := g.WithdrawOrder.
		Where(g.WithdrawOrder.ID.Eq(num)).
		Where(g.WithdrawOrder.Status.Eq(int32(rpc.WithDrawStatus_Lock))).
		Update(g.WithdrawOrder.Status, rpc.WithDrawStatus_Pass)
	if err2 != nil {
		return ret, xerrors.ErrWithDrawOrderNot
	}
	return ret, nil
}
func (l *WithdrawModel) WithdrawRefuse(in *rpc.RefuseWithDrawReq) (*rpc.RefuseWithDrawRes, error) {
	// todo: add your logic here and delete this line

	ret := &rpc.RefuseWithDrawRes{}
	g := query.Use(l.db)
	num, _ := strconv.ParseInt(in.OrderNos, 10, 64)
	_, err2 := g.WithdrawOrder.
		Where(g.WithdrawOrder.ID.Eq(num)).
		Where(g.WithdrawOrder.Status.Eq(int32(rpc.WithDrawStatus_Lock))).
		Update(g.WithdrawOrder.Status, rpc.WithDrawStatus_Refuse)
	if err2 != nil {
		return ret, xerrors.ErrWithDrawOrderNot
	}
	return ret, nil
}
func (l *WithdrawModel) WithdrawSuccess(in *rpc.SuccessWithDrawReq) (*rpc.SuccessWithDrawRes, error) {
	// todo: add your logic here and delete this line

	ret := &rpc.SuccessWithDrawRes{}
	g := query.Use(l.db)
	num, _ := strconv.ParseInt(in.OrderNo, 10, 64)

	h := g.WithdrawOrder.Where(g.WithdrawOrder.ID.Eq(num))
	d, err := h.First()
	if err != nil {
		return nil, xerrors.ErrWithDrawOrderNot
	}
	d.Status = int32(rpc.WithDrawStatus_Success)
	d.PayRemark = in.Data
	err2 := h.Save(d)
	if err2 != nil {
		return nil, xerrors.ErrWithDrawOrderNotSave
	}

	return ret, nil
}
func (l *WithdrawModel) WithdrawTypeUpdate(in *rpc.UpdateWithDrawTypeRequest) (*rpc.UpdateWithDrawTypeResponse, error) {
	// todo: add your logic here and delete this line

	ret := &rpc.UpdateWithDrawTypeResponse{}
	Withdraw := &modelWithdraw.WithdrawType{
		WithdrawID:     in.WithDrawType.WithdrawId,
		SelectedIcon:   in.WithDrawType.SelectedIcon,
		UnselectedIcon: in.WithDrawType.UnselectedIcon,
		Type:           in.WithDrawType.Type,
		DailyTimes:     in.WithDrawType.DailyTimes,
		MinAmount:      in.WithDrawType.MinAmount,
		MaxAmount:      in.WithDrawType.MaxAmount,
		FeeRate:        in.WithDrawType.FeeRate,
		Status:         in.WithDrawType.Status,
		Sort:           in.WithDrawType.Sort,
	}
	l.db.Create(Withdraw)
	return ret, nil
}
func (l *WithdrawModel) WithdrawUnlock(in *rpc.UnlockWithDrawReq) (*rpc.UnlockWithDrawRes, error) {
	// todo: add your logic here and delete this line

	ret := &rpc.UnlockWithDrawRes{}
	err := l.db.Transaction(func(tx *gorm.DB) error {
		g := query.Use(tx)
		for i, _ := range in.OrderNos {
			AssetsCWithdrawOrder, err := g.WithdrawOrder.
				Where(g.WithdrawOrder.OrderNo.Eq(in.OrderNos[i])).
				Where(g.WithdrawOrder.Status.Eq(int32(rpc.WithDrawStatus_Lock))).
				First()
			if err != nil {
				return xerrors.ErrWithDrawOrderNot
			} else {
				AssetsCWithdrawOrder.Locker = in.Locker
				AssetsCWithdrawOrder.Status = int32(rpc.WithDrawStatus_UnLock)
				err = g.WithdrawOrder.Save(AssetsCWithdrawOrder)
				if err != nil {
					return xerrors.ErrWithDrawOrderNotSave
				}
			}
			ret.Data = append(ret.Data, &rpc.WithdrawInfo{
				OrderNo: in.OrderNos[i],
				Status:  AssetsCWithdrawOrder.Status,
				ErrCode: ret.ResHead.Code,
			})
		}

		return nil
	})
	if err != nil {
		return nil, err
	}
	// -----
	return ret, nil
}
func (l *WithdrawModel) WithdrawUserListPage(in *rpc.UserFindWithDrawReq) (*rpc.UserFindWithDrawRes, error) {
	// todo: add your logic here and delete this line

	ret := &rpc.UserFindWithDrawRes{}
	if in.Page.Page <= 0 {
		in.Page.Page = 1
	}
	if in.Page.PageSize <= 0 {
		in.Page.PageSize = 10
	}
	g := query.Use(l.db)
	var AssetsCWithdrawOrder []*modelWithdraw.WithdrawOrder
	var err error
	if in.Page.SortOrder == "asc" {
		AssetsCWithdrawOrder, err = g.WithdrawOrder.Where(g.WithdrawOrder.UserId.Eq(in.UserId)).Order(g.WithdrawOrder.CreatedAt.Asc()).Limit(int(in.Page.PageSize)).Find()
	} else {
		AssetsCWithdrawOrder, err = g.WithdrawOrder.Where(g.WithdrawOrder.UserId.Eq(in.UserId)).Order(g.WithdrawOrder.CreatedAt.Desc()).Limit(int(in.Page.PageSize)).Find()

	}
	if err != nil {
		wlog.Handle.Errorf(l.ctx, "[%d:%d]:%s\n", in.UserId, ret.ResHead.Code, err.Error())
		return ret, err
	}
	start := (in.Page.Page - 1) * in.Page.PageSize // -------开始序号----------------
	if in.Page.PageSize > int64(len(AssetsCWithdrawOrder)) {
		in.Page.PageSize = int64(len(AssetsCWithdrawOrder))
	}
	// ret.Data = make([]*rpc.WithdrawInfo, in.Page.PageSize)
	for i := start; i < start+in.Page.PageSize; i++ {
		ret.Data = append(ret.Data, &rpc.WithdrawInfo{
			OrderNo:     AssetsCWithdrawOrder[i].OrderNo,
			UserId:      AssetsCWithdrawOrder[i].UserId,
			Amount:      AssetsCWithdrawOrder[i].OrderAmount,
			Type:        AssetsCWithdrawOrder[i].PayType,
			Account:     AssetsCWithdrawOrder[i].AccNo,
			ChannelCode: AssetsCWithdrawOrder[i].AccType,
			Status:      AssetsCWithdrawOrder[i].Status,
			CreatedAt:   AssetsCWithdrawOrder[i].CreatedAt,
		})
	}
	return ret, nil
}
